﻿using System;
using System.Globalization;

namespace QAToolsAddin {
    class Parser {
        public static bool ToText(object value, CultureInfo culture, out string text) {
            text = string.Empty;
            if (value is decimal)
                text = ((decimal)value).ToString("C", culture);
            else if (value is double)
                text = ((double)value).ToString("G", culture);
            else if (value is DateTime) {
                text = ((DateTime)value).ToString("s", culture);
            } else if (value is bool)
                text = ((bool)value).ToString(culture);
            else if (value is string == false)
                text = value.ToString();
            else return false;
            return true;
        }

        public static bool ToDateTime(object value, CultureInfo culture, out DateTime datetime) {
            datetime = DateTime.MinValue;
            if (value is DateTime)
                datetime = (DateTime)value;
            else if (value is string) {
                DateTime outDateTime;
                if (!DateTime.TryParse((string)value, culture, DateTimeStyles.None, out outDateTime)) return false;
                datetime = outDateTime;
            } else if (value is double) {
                try {
                    datetime = DateTime.FromOADate((double)value);
                } catch { return false; }
            } else return false;
            return true;
        }

        public static bool ToBoolean(object value, out bool boolean) {
            boolean = false;
            if (value is bool)
                boolean = (bool)value;
            else if (value is string) {
                if ((string)value == "1")
                    boolean = true;
                else if ((string)value == "0")
                    boolean = false;
                else {
                    bool outBool;
                    if (!bool.TryParse((string)value, out outBool)) return false;
                    boolean = outBool;
                }
            } else if (value is double) {
                if ((double)value == 1)
                    boolean = true;
                else if ((double)value == 0)
                    boolean = false;
                else return false;
            } else return false;
            return true;
        }

        public static bool Number(object value, out object number) {
            number = 0;
            if (value is double)
                number = (double)value;
            else if (value is string)
                return false;
            else if (value is bool) {
                if ((bool)value)
                    number = 1;
                else
                    number = 0;
            } else if (value is DateTime)
                number = ((DateTime)value).ToOADate();
            else if (value is int)
                number = (double)(int)value;
            else if (value is decimal)
                number = (double)(decimal)value;
            else
                return false;
            return true;
        }

        public static bool ToNumber(object value, char decimalSeparator, bool extract, out double number) {
            number = 0;
            if (value == null)
                return false;
            if (value is double)
                number = (double)value;
            else if (value is string) {
                string str = string.Empty;
                char previous = ' ';
                bool haspercent = false;
                if (extract) {
                    foreach (char car in (string)value) {
                        if (car == decimalSeparator) str += '.';
                        else if (car == '%' && str.Length != 0) {
                            haspercent = true;
                            break;
                        } else if ((car >= '0' && car <= '9') || car == '-' || car == '+') str += car;
                        else if ((car == 'E' || car == 'e') && (previous >= '0' && previous <= '9')) str += 'E';
                        previous = car;
                    }
                } else {
                    foreach (char car in (string)value) {
                        if (car == decimalSeparator) str += '.';
                        else if (car == ' ' || car == '.' || car == ',' || car == '%') continue;
                        else str += car;
                    }
                    haspercent = ((string)value).EndsWith("%");
                }
                double outDouble;
                if (!double.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out outDouble)) return false;
                number = haspercent ? outDouble / 100d : outDouble;
            } else if (value is bool)
                number = (bool)value ? 1 : 0;
            else if (value is DateTime)
                number = ((DateTime)value).ToOADate();
            else if (value is int)
                number = (double)(int)value;
            else if (value is decimal)
                number = (double)(decimal)value;
            else
                return false;
            return true;
        }

        public static bool ToCurrency(object value, char decimalSeparator, out decimal number) {
            number = 0;
            if (value is double)
                number = (decimal)(double)value;
            else if (value is decimal)
                number = (decimal)value;
            else if (value is string) {
                string str = string.Empty;
                foreach (char car in (string)value) {
                    if (car >= '0' && car <= '9') str += car;
                    else if (car == decimalSeparator) str += ".";
                }
                if (str.Length != 0) {
                    decimal outDecimal;
                    if (!decimal.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out outDecimal)) return false;
                    number = outDecimal;
                }
            } else return false;
            return true;
        }

        public static bool ToNumberFromHexa(object value, out object number) {
            number = 0d;
            if (value is double)
                number = (double)value;
            else if (value is string) {
                string strnumber = ((string)value).Replace("0x", "");
                if (strnumber.Length <= 8) {
                    int outInt;
                    if (!int.TryParse(strnumber, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out outInt)) return false;
                    number = (double)outInt;
                } else {
                    long outLong;
                    if (!long.TryParse(strnumber, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out outLong)) return false;
                    number = (double)outLong;
                }
            } else if (value is bool) {
                if ((bool)value) number = 1d;
                else number = 0d;
            } else return false;
            return true;
        }

        public static string ToHexa(long value) {
            if (value > int.MaxValue || value < int.MinValue)
                return "'" + ((long)value).ToString("X16");
            else
                return "'" + unchecked((int)value).ToString("X8");
        }

        public static string ToBinary(long value) {
            string binary = string.Empty;
            int group = 0;
            while (value > 0) {
                if ((group++ % 4) == 0)
                    binary = " " + binary;
                binary = ((long)(value % 2)).ToString() + binary;
                value = value / 2;
            }
            int left = group % 4;
            if (left != 0)
                binary = new string('0', 4 - left) + binary;
            binary = "'" + binary;
            return binary;
        }

        public static bool ToNumberFromBinary(object value, out object number) {
            number = 0d;
            if (value is double)
                number = (double)value;
            else if (value is string) {
                try {
                    number = (double)Convert.ToInt64(((string)value).Replace(" ", ""), 2);
                } catch { return false; }
            } else if (value is bool) {
                if ((bool)value) number = 1d;
                else number = 0d;
            } else return false;
            return true;
        }

        public static bool ToIdentical(object value, out object number) {
            number = value;
            return true;
        }


    }
}
